<?php
// +----------------------------------------------------------------------
// | Bwsaas
// +----------------------------------------------------------------------
// | Copyright (c) 2015~2020 http://www.buwangyun.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Gitee ( https://gitee.com/buwangyun/bwsaas )
// +----------------------------------------------------------------------
// | Author: buwangyun <hnlg666@163.com>
// +----------------------------------------------------------------------
// | Date: 2020-9-28 10:55:00
// +----------------------------------------------------------------------

namespace app\manage\model\admin;

use buwang\base\BaseModel;
use buwang\util\CommonTool;
use buwang\util\crud\BuildCrud;
use think\Exception;

class CodeGenerateTable extends BaseModel
{
    /**
     * 数据表主键
     * @var string
     */
    protected $pk = 'id';

    /**
     * 模型名称
     * @var string
     */
    protected $name = 'code_generate_table';

    //设置json类型字段
    protected $json = ['menu', 'tree', 'relations', 'delete'];
    //设置JSON数据返回数组
    protected $jsonAssoc = true;

    // 压缩包名称
    protected $zipTempName;

    // 压缩包临时路径
    protected $zipTempPath;

    /**
     * 关联数据表字段
     */
    public function tableColumn()
    {
        return $this->hasMany(CodeGenerateColumn::class, 'table_id', 'id');
    }

    /**
     * 初始化代码生成数据表信息
     * @param $tableData
     * @param $adminId
     * @return mixed
     */
    public static function initTable($tableData, $adminId)
    {
        return self::create([
            'table_name' => $tableData['name'],
            'table_comment' => $tableData['comment'],
            'template_type' => 0,
            'generate_type' => 0,
            'module_name' => 'adminapi',
            'admin_id' => $adminId,
            // 菜单配置
            'menu' => [
                'pid' => 0, // 父级菜单id
                'type' => 0, // 构建方式 0-手动添加 1-自动构建
                'name' => $tableData['comment'], // 菜单名称
            ],
            // 删除配置
            'delete' => [
                'type' => 0, // 删除类型
                'name' => 'delete_time', // 默认删除字段名
            ],
            // 关联配置
            'relations' => [],
            // 树形crud
            'tree' => []
        ]);
    }

    /**
     * 模板类型描述
     * @param $value
     * @param $data
     * @return mixed
     */
    public function getTemplateTypeDescAttr($value, $data)
    {
        $data_new = [
            0 => '单表(增删改查)',
            1 => '树表(增删改查)',
        ];
        if ($value === true) {
            return $data_new;
        }
        return $data_new[$data['template_type']] ?? '';
    }

    /**
     * 同步表字段
     */
    public static function syncColumnCodeGenerateTable($params)
    {
        // table 信息
        $table = self::findOrEmpty($params['id']);
        // 删除旧字段
        CodeGenerateColumn::whereIn('table_id', $table['id'])->delete();
        // 获取当前数据表字段信息
        $column = CodeGenerateColumn::getTableColumn($table['table_name']);
        // 创建新字段数据
        CodeGenerateColumn::initTableColumn($column, $table['id']);
        return true;
    }

    /**
     * 更新表字段
     * @param $params
     * @return bool
     */
    public static function editCodeGenerateTable($params)
    {
        // 格式化配置
        $options = self::formatTableData($params);
        // 更新主表-数据表信息
        self::update([
            'id' => $params['id'],
            'table_name' => $params['table_name'],
            'table_comment' => $params['table_comment'],
            'template_type' => $params['template_type'],
            'author' => $params['author'] ?? '',
            'remark' => $params['remark'] ?? '',
            'generate_type' => $params['generate_type'],
            'module_name' => $params['module_name'],
            'class_dir' => $params['class_dir'] ?? '',
            'class_comment' => $params['class_comment'] ?? '',
            'menu' => $options['menu'],
            'delete' => $options['delete'],
            'tree' => $options['tree'],
            'relations' => $options['relations'],
        ]);

        // 更新从表-数据表字段信息
        foreach ($params['table_column'] as $item) {
            CodeGenerateColumn::update([
                'id' => $item['id'],
                'column_comment' => $item['column_comment'] ?? '',
                'is_required' => $item['is_required'] ?? 0,
                'is_insert' => $item['is_insert'] ?? 0,
                'is_update' => $item['is_update'] ?? 0,
                'is_lists' => $item['is_lists'] ?? 0,
                'is_query' => $item['is_query'] ?? 0,
                'query_type' => $item['query_type'],
                'view_type' => $item['view_type'],
                'dict_type' => $item['dict_type'] ?? '',
            ]);
        }
        return true;
    }

    /**
     * 详情字段配置
     * @param $options
     * @return mixed
     */
    public static function formatTableData($options)
    {
        // 菜单配置
        $menuConfig = $options['menu'] ?? [];
        // 删除配置
        $deleteConfig = $options['delete'] ?? [];
        // 关联配置
        $relationsConfig = $options['relations'] ?? [];
        // 树表crud配置
        $treeConfig = $options['tree'] ?? [];

        $relations = [];
        foreach ($relationsConfig as $relation) {
            $relations[] = [
                'name' => $relation['name'] ?? '',
                'model' => $relation['model'] ?? '',
                'type' => $relation['type'] ?? 'has_one',
                'local_key' => $relation['local_key'] ?? 'id',
                'foreign_key' => $relation['foreign_key'] ?? 'id',
            ];
        }

        $options['menu'] = [
            'pid' => intval($menuConfig['pid'] ?? 0),
            'type' => intval($menuConfig['type'] ?? 0),
            'name' => !empty($menuConfig['name']) ? $menuConfig['name'] : $options['table_comment'],
        ];
        $options['delete'] = [
            'type' => intval($deleteConfig['type'] ?? 0),
            'name' => !empty($deleteConfig['name']) ? $deleteConfig['name'] : 'delete_time',
        ];
        $options['relations'] = $relations;
        $options['tree'] = [
            'tree_id' => $treeConfig['tree_id'] ?? "",
            'tree_pid' =>$treeConfig['tree_pid'] ?? "",
            'tree_name' => $treeConfig['tree_name'] ?? '',
        ];

        return $options;
    }

    /**
     * @notes 预览文件
     * @param $data
     * @param $table_name
     * @param $is_preview:1=预览，2=生成
     */
    public function preview($data, $table_name, $is_preview = 1)
    {
        //应用目录名
        //$appName = $data['appName'] ?? 'generate';
        if(isset($data['appName']) && $data['appName']){
            $appName = $data['appName'];
        }else{
            $appName ='generate';
        }

        $table = $data['table'];
        //$controllerFilename = $data['controllerFilename']??'';
        //首字母大写，下划线转驼峰；不自定义会新增目录
        $controllerFilename = ucfirst(CommonTool::lineToHump($table_name))??'';
        $modelFilename = $data['modelFilename']??'';
        $checkboxFieldSuffix = $data['checkboxFieldSuffix']??'';
        $radioFieldSuffix = $data['radioFieldSuffix']??'';
        $imageFieldSuffix = $data['imageFieldSuffix']??'';
        $imagesFieldSuffix = $data['imagesFieldSuffix']??'';
        $fileFieldSuffix = $data['fileFieldSuffix']??'';
        $filesFieldSuffix = $data['filesFieldSuffix']??'';
        $dateFieldSuffix = $data['dateFieldSuffix']??'';
        $switchFields = $data['switchFields']??'';
        $selectFileds = $data['selectFileds']??'';
        $sortFields = $data['sortFields']??'';
        $ignoreFields = $data['ignoreFields']??[];

        $relationTable = $data['relationTable']??[];
        $foreignKey = $data['foreignKey']??'';
        $primaryKey = $data['primaryKey']??'';
        $relationModelFilename = $data['relationModelFilename']??'';
        $relationOnlyFileds = $data['relationOnlyFileds']??'';
        $relationBindSelect = $data['relationBindSelect']??'';

        //是否强制生成
        $force = $data['force']??true;
        $delete = $data['delete']??'';
        $relations = [];
        foreach ($relationTable as $key => $val) {
            $relations[] = [
                'table' => $relationTable[$key],
                'foreignKey' => isset($foreignKey[$key]) ? $foreignKey[$key] : null,//存在于主表的外键（系统只生成belongsTo关联），一对多关联（其中“一”是主表，“多”是从表）
                'primaryKey' => isset($primaryKey[$key]) ? $primaryKey[$key] : null,//关联表的主键
                'modelFilename' => isset($relationModelFilename[$key]) ? $relationModelFilename[$key] : null,//自定义关联模型文件名
                'onlyFileds' => isset($relationOnlyFileds[$key]) ? explode(",", $relationOnlyFileds[$key]) : [],//关联模型中只显示的字段
                'relationBindSelect' => isset($relationBindSelect[$key]) ? $relationBindSelect[$key] : null,//关联模型中的字段用于主表外键的表单下拉选择
            ];
        }
        if (empty($table)) {
            throw new Exception('请设置主表');
        }

        try {
            $build = (new BuildCrud())
                ->setAppName($appName)//设置应用目录名
                ->setTable($table)
                ->setForce($force);

            !empty($controllerFilename) && $build = $build->setControllerFilename($controllerFilename);
            !empty($modelFilename) && $build = $build->setModelFilename($modelFilename);

            !empty($checkboxFieldSuffix) && $build = $build->setCheckboxFieldSuffix($checkboxFieldSuffix);
            !empty($radioFieldSuffix) && $build = $build->setRadioFieldSuffix($radioFieldSuffix);
            !empty($imageFieldSuffix) && $build = $build->setImageFieldSuffix($imageFieldSuffix);
            !empty($imagesFieldSuffix) && $build = $build->setImagesFieldSuffix($imagesFieldSuffix);
            !empty($fileFieldSuffix) && $build = $build->setFileFieldSuffix($fileFieldSuffix);
            !empty($filesFieldSuffix) && $build = $build->setFilesFieldSuffix($filesFieldSuffix);
            !empty($dateFieldSuffix) && $build = $build->setDateFieldSuffix($dateFieldSuffix);
            !empty($switchFields) && $build = $build->setSwitchFields($switchFields);
            !empty($selectFileds) && $build = $build->setSelectFileds($selectFileds);
            !empty($sortFields) && $build = $build->setSortFields($sortFields);
            //单独处理下不显示的字段赋值问题
            $ignoreFields = count($ignoreFields) == 1 ? explode(",", $ignoreFields[0]) : $ignoreFields;
            !empty($ignoreFields) && $build = $build->setIgnoreFields($ignoreFields);
            //单独处理关联模型相关
            foreach ($relations as $relation) {
                $build = $build->setRelation($relation['table'], $relation['foreignKey'], $relation['primaryKey'], $relation['modelFilename'], $relation['onlyFileds'], $relation['relationBindSelect']);
            }
            //开始真正的文件渲染生成
            $build = $build->renderNew();
            //生成的文件名存储的数组
            $fileList = $build->getFileList();
            //生成代码
            if($is_preview == 2){
                $result = $build->createNew();
                $this->zipFile();
                $zipFile = $this->getDownloadUrl();
                return $zipFile;
            }
            return $fileList;
        } catch (\Exception $e) {
            throw new Exception('生成代码异常:'.$e->getMessage());
        }
    }


    /**
     * 压缩文件
     */
    public function zipFile()
    {
        $generatePath = root_path() . 'runtime/generate/';
        $appPath = root_path() . 'runtime/';

        $fileName = 'curd-' . date('YmdHis') . '.zip';
        $this->zipTempName = $fileName;
        $this->zipTempPath = $generatePath . $fileName;
        $zip = new \ZipArchive();
        $zip->open($this->zipTempPath, \ZipArchive::CREATE);
        $this->addFileZip($appPath, 'generate', $zip);
        $zip->close();
        return true;
    }

    /**
     * 往压缩包写入文件
     */
    public function addFileZip($basePath, $dirName, $zip)
    {
        $handler = opendir($basePath . $dirName);
        while (($filename = readdir($handler)) !== false) {
            if ($filename != '.' && $filename != '..') {
                if (is_dir($basePath . $dirName . '/' . $filename)) {
                    // 当前路径是文件夹
                    $this->addFileZip($basePath, $dirName . '/' . $filename, $zip);
                } else {
                    // 写入文件到压缩包
                    $zip->addFile($basePath . $dirName . '/' . $filename, $dirName . '/' . $filename);
                }
            }
        }
        closedir($handler);
        return true;
    }

    /**
     * 返回压缩包存储路径
     */
    public function getDownloadUrl()
    {
        $vars = ['file' => $this->zipTempName];
        return (string)url("/manage/admin/generator/downloadCodeGenerateTable", $vars, false, true);
    }

    /**
     * 下载文件
     */
    public static function download(string $fileName)
    {
        $path = root_path() . 'runtime/generate/' . $fileName;
        if (!file_exists($path)) {
            throw new Exception('下载失败：文件不存在');
        }
        return $path;
    }

    /**
     * 路由文件自动生成器
     *
     * @param string $module 模块名（如：admin）
     * @param string $controller 控制器名（如：member.Wallet）
     * @param array $actions 方法列表（如：['index', 'edit']）
     * @param array $middlewares 中间件配置
     * @param string $routePrefix 路由前缀
     * @return string 生成的路由代码
     */
    function generateRouteCode(
        string $module,
        string $controller,
        array $actions,
        array $middlewares = [],
        string $routePrefix = ''
    ): string {
        // 将控制器名称中的点转换为路径
        $controllerPath = str_replace('.', '/', $controller);
        $controllerPath = lcfirst($controllerPath);

        // 构建路由组前缀
        $fullPrefix = trim($module . '/' . $routePrefix, '/');
        // 生成路由规则
        $routeRules = [];
        foreach ($actions as $action) {
            //$routeRules[] = "        Route::rule('$action', '$module.$controller/$action');";
            $routeRules[] = "        Route::rule('$action', '$controller/$action');";
        }
        // 默认添加index路由
        if (in_array('index', $actions)) {
            //$routeRules[] = "        Route::rule('', '$module.$controller/index');";
            $routeRules[] = "        Route::rule('', '$controller/index');";
        }
        $routeRulesStr = implode("\n", $routeRules);
        // 生成中间件部分
        $middlewareStr = '';
        foreach ($middlewares as $middleware => $params) {
            if (is_numeric($middleware)) {
                $middlewareStr .= "->middleware($params::class)";
            } else {
                // 将布尔值转换为字符串 true/false
                if ($params === true) {
                    $paramsStr = 'true';
                } elseif ($params === false) {
                    $paramsStr = 'false';
                } elseif (is_array($params)) {
                    $paramsStr = implode(', ', array_map(function($item) {
                        return is_bool($item) ? ($item ? 'true' : 'false') : $item;
                    }, $params));
                } else {
                    $paramsStr = $params;
                }
                $middlewareStr .= "->middleware($middleware::class, $paramsStr)";
            }
        }

        // 构建完整路由代码
        $routeCode = <<<CODE
            <?php
            use think\\facade\\Route;
            Route::group('/', function () {
                Route::group('$controllerPath', function () {
            $routeRulesStr
                });
            })$middlewareStr;
            CODE;

        return $routeCode;
    }

}